package de.ama.util;

import com.github.savvasdalkitsis.jtmdb.Log;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
import sun.awt.image.BufferedImageGraphicsConfig;

import javax.imageio.*;
import javax.imageio.stream.FileImageOutputStream;
import javax.imageio.stream.ImageInputStream;
import javax.swing.*;
import java.awt.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import java.io.*;
import java.net.URL;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * Created by IntelliJ IDEA.
 * User: ama
 * Date: 15.02.11
 * Time: 19:08
 * To change this template use File | Settings | File Templates.
 */
public class Util {
    public static final String CRLF = "\r\n";


    public static ImageIcon loadIcon(String path) {
        if (isEmpty(path)) {
            return loadIcon("icons/film.png");
        }
        try {
            ImageIcon icon;
            if (new File(path).exists()) {
                icon = new ImageIcon(path);
            } else {
                icon = new ImageIcon(Util.class.getClassLoader().getResource(path));
            }
            if (icon.getImageLoadStatus() != MediaTracker.COMPLETE) {
                return loadIcon("icons/clock.png");
            }
            return icon;
        } catch (Exception exception) {
            Log.log(exception);
            return loadIcon("icons/error_add.png");
        }
    }

    public static ImageIcon loadIconURL(String url) {
        try {
            ImageIcon icon;
            icon = new ImageIcon(new URL(url));
            if (icon.getImageLoadStatus() != MediaTracker.COMPLETE) {
                Log.log("can not load icon for " + url);
                return null;
            }
            return icon;
        } catch (Exception exception) {
            Log.log(exception);
            return null;
        }
    }

    public static InputStream inputStream(URL url) {
        try {
            return url.openStream();
        } catch (Exception exception) {
            Log.log(exception);
            return null;
        }
    }


    public static void writeCompressedJpeg(InputStream inStream, File outfile, float quality) {
        try {
            ImageReader imageReader;
            ImageInputStream imageInputStream = ImageIO.createImageInputStream(inStream);
            Iterator readers = ImageIO.getImageReaders(imageInputStream);
            if (readers.hasNext()) {
                imageReader = (ImageReader) readers.next();
            } else {
                throw new RuntimeException("this seems not to be an Imageformat ");
            }
            imageReader.setInput(imageInputStream, true);

            BufferedImage input = imageReader.read(0);
            // Get a ImageWriter for jpeg format.
            Iterator<ImageWriter> writers = ImageIO.getImageWritersBySuffix("jpeg");
            if (!writers.hasNext()) throw new IllegalStateException("No writers found");
            ImageWriter writer = writers.next();
            // Create the ImageWriteParam to compress the image.
            ImageWriteParam param = writer.getDefaultWriteParam();
            param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
            param.setCompressionQuality(quality);


            FileImageOutputStream output = new FileImageOutputStream(outfile);
            writer.setOutput(output);
            writer.write(null, new IIOImage(input, null, null), param);
        } catch (IOException e) {
            Log.log(e);
        }
    }


    public static void writeCompressedAndScaledJpeg(InputStream instream, File outfile, int width, int height, float quality) {
        try {
            ImageReader imageReader;
            ImageInputStream imageInputStream = ImageIO.createImageInputStream(instream);
            Iterator readers = ImageIO.getImageReaders(imageInputStream);
            if (readers.hasNext()) {
                imageReader = (ImageReader) readers.next();
            } else {
                throw new RuntimeException("this seems not to be an Imageformat ");
            }
            imageReader.setInput(imageInputStream, true);
            BufferedImage input = imageReader.read(0);

            BufferedImage resized = resize(input,width,height);

            Iterator<ImageWriter> writers = ImageIO.getImageWritersBySuffix("jpeg");
            if (!writers.hasNext()) throw new IllegalStateException("No writers found");
            ImageWriter writer = writers.next();
            ImageWriteParam param = writer.getDefaultWriteParam();
            param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
            param.setCompressionQuality(quality);

            FileImageOutputStream output = new FileImageOutputStream(outfile);
            writer.setOutput(output);
            writer.write(null, new IIOImage(resized, null, null), param);
        } catch (IOException e) {
            Log.log(e);
        }
    }

    public static BufferedImage resizeImage(BufferedImage image, int width, int height) {
        image = createCompatibleImage(image);
        image = resize(image, 100, 100);
        image = blurImage(image);
        return resize(image, width, height);
    }

    public static BufferedImage blurImage(BufferedImage image) {
        float ninth = 1.0f / 9.0f;
        float[] blurKernel = {
                ninth, ninth, ninth,
                ninth, ninth, ninth,
                ninth, ninth, ninth
        };

        Map<RenderingHints.Key, Object> map = new HashMap<RenderingHints.Key, Object>();
        map.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        map.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        map.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        RenderingHints hints = new RenderingHints(map);
        BufferedImageOp op = new ConvolveOp(new Kernel(3, 3, blurKernel), ConvolveOp.EDGE_NO_OP, hints);
        return op.filter(image, null);
    }


    ////////////////////////////////////////// Helper //////////////////////////////////////////////////

    private static BufferedImage resize(BufferedImage image, int width, int height) {
        int type = image.getType() == 0 ? BufferedImage.TYPE_INT_ARGB : image.getType();
        BufferedImage resizedImage = new BufferedImage(width, height, type);
        Graphics2D g = resizedImage.createGraphics();
        g.setComposite(AlphaComposite.Src);
        g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g.drawImage(image, 0, 0, width, height, null);
        g.dispose();
        return resizedImage;
    }


    private static BufferedImage createCompatibleImage(BufferedImage image) {
        GraphicsConfiguration gc = BufferedImageGraphicsConfig.getConfig(image);
        int w = image.getWidth();
        int h = image.getHeight();
        BufferedImage result = gc.createCompatibleImage(w, h, Transparency.TRANSLUCENT);
        Graphics2D g2 = result.createGraphics();
        g2.drawRenderedImage(image, null);
        g2.dispose();
        return result;
    }

    private static BufferedImage getTiledInstance(BufferedImage img,
                                                  int targetWidth,
                                                  Object hint) {
        int type = (img.getTransparency() == Transparency.OPAQUE) ?
                BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;

        BufferedImage ret = img;

        int w = img.getWidth();
        int h = img.getHeight();
        Log.log("initial image width(" + w + ")");

        while (w > targetWidth) {
            w /= 2;
            h /= 2;
            if (w < targetWidth) {
                break;
            }

            Log.log("scale image down to width(" + w + ") ");
            BufferedImage tmp = new BufferedImage(w, h, type);
            Graphics2D g2 = tmp.createGraphics();
            g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
            g2.drawImage(ret, 0, 0, w, h, null);
            g2.dispose();
            ret = tmp;
        }

        return ret;
    }

    public static boolean isEmpty(Object o) {
        if (o == null) return true;
        if (o instanceof String) {
            String s = (String) o;
            return s.length() == 0;
        }
        if (o instanceof Collection) {
            Collection s = (Collection) o;
            return s.size() == 0;
        }
        return false;
    }

    public static String getFileExtension(File file) {
        return getFileExtension(file.getName());
    }
    public static String getFileExtension(String name) {
        int i = name.lastIndexOf('.');
        if(i>0) {
            return name.substring(i +1);
        }
        return "";
    }

    public static String removeTrailingStringIgnoreCase(String in, String remove) {
        if (in.toLowerCase().endsWith(remove.toLowerCase())) {
            in = in.substring(0, in.length() - remove.length());
        }
        return in;
    }

    public static String safeToString(String in) {
        return in == null ? "" : in;
    }

    public static String safeToString(String in, String def) {
        return in == null ? def : in;
    }

    public static String escapeText(String str) {

        char[] strBuffer = str.toCharArray();
        int oldLength = strBuffer.length;
        int newLength = oldLength;

        /**
         * Da davon ausgegangen werden kann, dass in den meisten Fällen nichts gemacht werden muss,
         * wird in einem ersten Schritt geprüft, ob ein Escaping durchgeführt werden muss und
         * wie lang die dann resultierende Zeichenkette sein muss.
         */
        for (int i = 0; i < oldLength; i++) {
            char c = strBuffer[i];
            switch (c) {
                case '&':
                    newLength += 4;
                    break;
                case '"':
                    newLength += 5;
                    break;
                case '>':
                    newLength += 3;
                    break;
                case '<':
                    newLength += 3;
                    break;
                case 0x9:
                case 0xA:
                case 0xD:
                    break;
                default:
                    if (((c >= 0x20) && (c <= 0xD7FF)) || ((c >= 0xE000) && (c <= 0xFFFD))) {
                        break;
                    } else throw new RuntimeException("Invalid character '" + c + "' in: " + str);
            }
        }

        /**
         * Wenn sich die Länge nicht verändert hat,
         * kann der Vorgang des escapens abgekützt werden.
         */
        if (oldLength == newLength) {
            /**
             * Wenn keine Änderungen gemacht werden müssen,
             * dann kann an dieser Stelle der original
             * String returniert werden.
             */
            return new String(strBuffer);
        }

        int putIndex = 0;
        char[] newBuffer = new char[newLength];

        for (int i = 0; i < oldLength; i++) {
            char c = strBuffer[i];

            switch (c) {
                case '&':
                    newBuffer[putIndex++] = '&';
                    newBuffer[putIndex++] = 'a';
                    newBuffer[putIndex++] = 'm';
                    newBuffer[putIndex++] = 'p';
                    newBuffer[putIndex++] = ';';
                    break;
                case '"':
                    newBuffer[putIndex++] = '&';
                    newBuffer[putIndex++] = 'q';
                    newBuffer[putIndex++] = 'u';
                    newBuffer[putIndex++] = 'o';
                    newBuffer[putIndex++] = 't';
                    newBuffer[putIndex++] = ';';
                    break;
                case '>':
                    newBuffer[putIndex++] = '&';
                    newBuffer[putIndex++] = 'g';
                    newBuffer[putIndex++] = 't';
                    newBuffer[putIndex++] = ';';
                    break;
                case '<':
                    newBuffer[putIndex++] = '&';
                    newBuffer[putIndex++] = 'l';
                    newBuffer[putIndex++] = 't';
                    newBuffer[putIndex++] = ';';
                    break;
                case 0x9:
                case 0xA:
                case 0xD:
                default:
                    newBuffer[putIndex++] = c;
                    break;
            }

        }

        return new String(newBuffer);
    }

    public static String asString(Date date) {
        if (Util.isEmpty(date)) return "";
        DateFormat dateFormat = new SimpleDateFormat("MM.yyyy.dd");
        return dateFormat.format(date);
    }

    public static String asString(Collection col) {
        String ret = "";
        for (Iterator iter = col.iterator(); iter.hasNext();) {
            if (ret.length() > 0) ret += ", ";
            ret += iter.next();
        }
        return ret;
    }

    public static void sleep(int i) {
        try {
            Thread.sleep(i);
        } catch (InterruptedException e) {

        }
    }


    public static final String CHARS_LOWER = "abcdefghijklmnopqrstuvwxyz";
    public static final String CHARS_UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    public static final String CHARS_ALPHA = CHARS_UPPER + CHARS_LOWER + ".,-:*?";
    public static final String CHARS_NUM = "1234567890";
    public static final String CHARS_UMLAUT = "äöüßÄÖÜ";

    public static final String CHARS_SEARCH = CHARS_ALPHA + CHARS_NUM + CHARS_UMLAUT + " ";

    public static boolean isNumeric(char c) {
        return (CHARS_NUM.indexOf(c) >= 0);
    }

    public static boolean isNumeric(String aString) {
        char[] chars = aString.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            if (!isNumeric(chars[i])) {
                return false;
            }
        }
        return true;
    }

    public static boolean isAlpha(char c) {
        return (CHARS_ALPHA.indexOf(c) >= 0);
    }

    public static boolean isLower(char c) {
        return (CHARS_LOWER.indexOf(c) >= 0);
    }

    public static boolean isUpper(char c) {
        return (CHARS_UPPER.indexOf(c) >= 0);
    }

    public static boolean isSearchChar(char c) {
        return (CHARS_SEARCH.indexOf(c) >= 0);
    }

    public static boolean isAlphaNumeric(char c) {
        return isAlpha(c) || isNumeric(c);
    }

    public static boolean isUmlaut(char c) {
        return (CHARS_UMLAUT.indexOf(c) >= 0);
    }


    public static boolean isAlpha(String aString) {
        char[] chars = aString.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            if (!isAlpha(chars[i])) {
                return false;
            }
        }
        return true;
    }

    public static String spaces = "                                                                                                   ";

    public static String formatString(String in, int len) {
        return formatString(in, len, true);
    }

    public static String formatString(String in, int len, boolean cut) {
        if (in == null) in = "null";
        String out = in;
        if (cut && in.length() > len) {
            out = in.substring(0, len);
        } else if (in.length() < len) {
            out = in + spaces.substring(0, len - in.length());
        }
        return out;
    }

    public static void writeXStream(Object o, File file) {
        writeXStream(o, file, null);
    }

    public static void writeXStream(Object o, File file, XStream stream) {
        if (stream == null) {
            stream = new XStream(new DomDriver());
        }
        try {
            stream.toXML(o, new OutputStreamWriter(new FileOutputStream(file), "UTF-8"));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

    public static void readXStream(Object o, File file) {
        readXStream(o, file, null);
    }

    public static Object readXStream(Object o, File file, XStream stream) {
        if (stream == null) {
            stream = new XStream(new DomDriver());
        }
        try {
            InputStream is = new FileInputStream(file);
            o = stream.fromXML(is);
            return o;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }


    public static Image getClipboardImage() {
        Transferable t = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null);

        try {
            if (t != null && t.isDataFlavorSupported(DataFlavor.imageFlavor)) {
                Image text = (Image) t.getTransferData(DataFlavor.imageFlavor);
                return text;
            }
        } catch (Exception e) {
        }
        return null;
    }

    public static void addMenuItem(MenuElement menu, String label, String iconpath, ActionListener a){
        JMenuItem item = new JMenuItem(label, Util.loadIcon(iconpath));
        item.addActionListener(a);
        if (menu instanceof JMenu) {
            JMenu jMenu = (JMenu) menu;
            jMenu.add(item);
        }
        if (menu instanceof JPopupMenu) {
            JPopupMenu jpMenu = (JPopupMenu) menu;
            jpMenu.add(item);
        }
    }

    public static void addPopupMenu(JComponent component, final JPopupMenu menu){
        component.addMouseListener(new MouseAdapter() {
            public void mousePressed(MouseEvent evt) {
                if (evt.isPopupTrigger()) {
                    menu.show(evt.getComponent(), evt.getX(), evt.getY());
                }
            }

            public void mouseReleased(MouseEvent evt) {
                if (evt.isPopupTrigger()) {
                    menu.show(evt.getComponent(), evt.getX(), evt.getY());
                }
            }
        });

    }

}
